package com.example.log.aspect;

import com.example.log.annotation.LogOperation;
import com.example.enums.LogTypeEnum;
import com.example.enums.OperationStatusEnum;
import com.example.log.entity.OperationLog;
import com.example.security.InfoDetail;
import com.example.security.SecurityUser;
import com.example.security.UserDetail;
import com.example.utils.HttpContextUtils;
import com.example.utils.IpUtils;
import com.example.utils.JsonUtils;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.http.HttpHeaders;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.*;
import com.example.log.service.LogPusService;

/**
 * 操作日志，切面处理类
 *
 * @author Mark sunlightcs@gmail.com
 * @since 1.0.0
 */
@Slf4j
@Aspect
@Component
public class LogOperationAspect {

    @Autowired
    private Environment environment;

    @Autowired
    private LogPusService logPusService;

    @Pointcut("@annotation(com.example.log.annotation.LogOperation)")
    public void logPointCut() {

    }

    @Around("logPointCut()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        long beginTime = System.currentTimeMillis();
        try {
            //执行方法
            Object result = point.proceed();

            //执行时长(毫秒)
            long time = System.currentTimeMillis() - beginTime;
            //保存日志
            saveLog(point, time, OperationStatusEnum.SUCCESS.value(), null);

            return result;
        }catch(Exception e) {
            //执行时长(毫秒)
            long time = System.currentTimeMillis() - beginTime;
            //保存日志
            saveLog(point, time, OperationStatusEnum.FAIL.value(), e.getMessage());

            throw e;
        }
    }


    private void saveLog(ProceedingJoinPoint joinPoint, long time, Integer status, String msg) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();

        OperationLog operationlog = new OperationLog();
        operationlog.setMsg(msg);
        // 注解上的描述
        LogOperation annotation = method.getAnnotation(LogOperation.class);
        if(annotation != null){
            operationlog.setOperation(annotation.value());
        }

        //登录用户信息
        InfoDetail user = SecurityUser.getUser();
        if(user != null){
            operationlog.setCreator(user.getId());
            operationlog.setCreatorName(user.getUsername());
        }

        operationlog.setType(LogTypeEnum.OPERATION.value());
        String name = environment.getProperty("spring.application.name");
        operationlog.setModule(name);
        operationlog.setStatus(status);
        operationlog.setRequestTime(time);
        operationlog.setCreateDate(new Date());

        //请求相关信息
        HttpServletRequest request = HttpContextUtils.getHttpServletRequest();
        operationlog.setIp(IpUtils.getIpAddr(request));
        operationlog.setUserAgent(request.getHeader(HttpHeaders.USER_AGENT));
        operationlog.setRequestUri(request.getRequestURI());
        String methodName = request.getMethod();
        operationlog.setRequestMethod(methodName);

        //请求参数
        if (List.of("GET", "DELETE").contains(methodName)){
            Map<String, String> parameterMap = new HashMap<>();
            Enumeration<String> parameterNames = request.getParameterNames();
            while (parameterNames.hasMoreElements()) {
                String paramName = parameterNames.nextElement();
                String paramValue = request.getParameter(paramName);
                parameterMap.put(paramName, paramValue);
            }

            operationlog.setRequestParams(parameterMap.isEmpty() ? null : JsonUtils.toJsonString(parameterMap));
        }

        if (List.of("POST", "PUT").contains(methodName)) {
            Object[] args = joinPoint.getArgs();
            operationlog.setRequestParams(JsonUtils.toJsonString(args));
        }

        //保存到Redis队列里
        log.info("logdfd {}", operationlog);
//        logProducer.saveLog(log);
        logPusService.convertAndSend(operationlog);
    }
}